/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://gitee.com/shenyczz/csharp_csk
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using CSharpKit.Extensions;

namespace CSharpKit.Convertions
{
    /// <summary>
    /// 字节流转换
    /// </summary>
    public sealed class BytesConverter
    {
        BytesConverter() { }
        public static readonly BytesConverter Instance = new BytesConverter();


        #region Value

        /// <summary>
        /// Support Boolean, Byte, Int16, Int32, Int64, UInt16, UInt32, UInt64, Single, Double
        /// </summary>
        /// <param name="TSource"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<byte> FromValue<T>(T TSource)
        {
            byte[] bytes = null;

            try
            {
                switch (typeof(T).Name)
                {
                    case TypeString.Boolean:
                        bytes = BitConverter.GetBytes((Boolean)(dynamic)TSource);
                        break;
                    case TypeString.Byte:
                        bytes = new Byte[] { ((Byte)(dynamic)TSource) };
                        break;
                    case TypeString.SByte:
                        bytes = new Byte[] { ((Byte)(SByte)(dynamic)TSource) };
                        break;
                    case TypeString.Int16:
                        bytes = BitConverter.GetBytes((Int16)(dynamic)TSource);
                        break;
                    case TypeString.Int32:
                        bytes = BitConverter.GetBytes((Int32)(dynamic)TSource);
                        break;
                    case TypeString.Int64:
                        bytes = BitConverter.GetBytes((Int64)(dynamic)TSource);
                        break;
                    case TypeString.UInt16:
                        bytes = BitConverter.GetBytes((UInt16)(dynamic)TSource);
                        break;
                    case TypeString.UInt32:
                        bytes = BitConverter.GetBytes((UInt32)(dynamic)TSource);
                        break;
                    case TypeString.UInt64:
                        bytes = BitConverter.GetBytes((UInt64)(dynamic)TSource);
                        break;
                    case TypeString.Single:
                        bytes = BitConverter.GetBytes((Single)(dynamic)TSource);
                        break;
                    case TypeString.Double:
                        bytes = BitConverter.GetBytes((Double)(dynamic)TSource);
                        break;

                    default:
#if DEBUG
                        Debug.WriteLine(typeof(T).Name);
#endif
                        break;
                }

            }
            catch (System.Exception)
            {
                // throw;
            }


            return bytes;
        }

        /// <summary>
        /// 字节数组转换为类型值
        /// </summary>
        /// <param name="fromBytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ToValue<T>(IEnumerable<byte> fromBytes)
        {
            T result = default(T);

            try
            {
                var union = get_union<T>();         // 取得对应联合

                var tlen = Marshal.SizeOf<T>();     // 类型字节长度
                var blen = fromBytes.Count();       // 字节数据长度
                var length = blen / tlen;           // 转换数组长度
                var bytes = fromBytes.ToArray();
                if (length > 0)
                {
                    union.SetBytes(bytes, 0);
                    result = union.GetValue<T>();
                }

            }
            catch (System.Exception)
            {
                // throw;
            }

            return result;
        }

        /// <summary>
        /// 其他类型数组转换为字节数组 <para/>
        /// Support bool[], Byte[], Int16[], Int32[], Int64[], UInt16[], UInt32[], UInt64[], Single[], Double[]
        /// </summary>
        /// <param name="TSources">其他类型数据集合</param>
        /// <typeparam name="T">泛型类型</typeparam>
        /// <returns></returns>
        public IEnumerable<byte> FromArray<T>(IEnumerable<T> TSources)
        {
            byte[] bytes = null;

            try
            {
                var temp = new List<byte>();
                TSources.ForEach(item => temp.AddRange(this.FromValue(item)));
                bytes = temp.ToArray();
            }
            catch (System.Exception)
            {
                // throw;
            }

            return bytes;
        }

        /// <summary>
        /// Int16[], Int32[], Int64[], UInt16[], UInt32[], UInt64[], Single[], Double[]
        /// </summary>
        /// <param name="fromBytes"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IEnumerable<T> ToArray<T>(IEnumerable<byte> fromBytes)
        {
            IEnumerable<T> result = null;

            try
            {
                var temp = new List<T>();

                var tlen = Marshal.SizeOf<T>();     // 类型字节长度
                var blen = fromBytes.Count();       // 字节数据长度
                var length = blen / tlen;           // 转换数组长度
                var union = get_union<T>();         // 取得对应联合
                var bytes = fromBytes.ToArray();
                for (var i = 0; i < length; i++)
                {
                    union.SetBytes(bytes, i * tlen);
                    temp.Add(union.GetValue<T>());
                }

                result = temp.ToArray();
            }
            catch (System.Exception)
            {
                // throw;
            }

            return result;
        }

        #endregion Value

        #region String

        /// <summary>
        /// 字符串转换到字节
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public Byte[] FromString(String str)
        {
#if NET31 || NET50 || NET60
            System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
#endif
            return Encoding.GetEncoding("GB18030").GetBytes(str);
        }


        /// <summary>
        /// 字节转换到字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public String ToString(Byte[] bytes)
        {
            var result = string.Empty;

            try
            {
                for (int i = 0; i < bytes?.Length; i++)
                {
                    if (bytes[i] == 0)
                    {
                        for (int pos = i; pos < bytes.Length; pos++)
                        {
                            bytes[pos] = 0;
                        }
                        break;
                    }
                }

#if NET31 || NET50 || NET60
                // 'GB18030' is not a supported encoding name.
                // For information on defining a custom encoding, 
                // see the documentation for the Encoding.RegisterProvider method. (Parameter 'name')
                System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
#endif
                result = Encoding.GetEncoding("GB18030").GetString(bytes).Trim(new char[] { '\0' });
            }
            catch (Exception ex)
            {
                string errMsg = ex.Message;
            }

            return result;
        }


        public String ToString(Byte[] bytes, int start, int length)
        {
            var spb = new Span<byte>(bytes);
            return ToString(spb.Slice(start, length).ToArray());
        }

        #endregion

        #region Struct

        /// <summary>
        /// Struct => Bytes
        /// </summary>
        /// <param name="raws"></param>
        /// <typeparam name="TStruct"></typeparam>
        /// <returns></returns>
        public IEnumerable<byte> FromStruct<TStruct>(TStruct raws)
        {
            var result = default(IEnumerable<byte>);

            try
            {
                var rawsize = Marshal.SizeOf<TStruct>();
                var pbuffer = Marshal.AllocHGlobal(rawsize);
                Marshal.StructureToPtr(raws, pbuffer, false);
                var rawdatas = new byte[rawsize];
                Marshal.Copy(pbuffer, rawdatas, 0, rawsize);
                Marshal.FreeHGlobal(pbuffer);
                result = rawdatas;
            }
            catch (System.Exception)
            {
                // throw;
            }

            return result;
        }

        /// <summary>
        /// Bytes => Struct
        /// </summary>
        /// <param name="rawdatas"></param>
        /// <typeparam name="TStruct"></typeparam>
        /// <returns></returns>
        public TStruct ToStruct<TStruct>(IEnumerable<byte> rawdatas)
        {
            var result = default(TStruct);

            try
            {
                var rawsize = Marshal.SizeOf<TStruct>();
                var buffer = Marshal.AllocHGlobal(rawsize);
                Marshal.Copy(rawdatas.ToArray(), 0, buffer, rawsize);
                var retobj = Marshal.PtrToStructure(buffer, typeof(TStruct));
                // var retobj = Marshal.PtrToStructure<TStruct>(buffer, typeof(TStruct));
                Marshal.FreeHGlobal(buffer);

                result = (TStruct)retobj;
            }
            catch (System.Exception)
            {
                // throw;
            }

            return result;
        }

        #endregion





        void tttt()
        {
            // KConverter
        }




        private IUnion get_union<T>()
        {
            IUnion result = default(IUnion);

            switch (typeof(T).Name)
            {
                case TypeString.Int16:
                    result = new UnionBytesInt16();
                    break;
                case TypeString.Int32:
                    result = new UnionBytesInt32();
                    break;
                case TypeString.Int64:
                    result = new UnionBytesInt64();
                    break;
                case TypeString.UInt16:
                    result = new UnionBytesUInt16();
                    break;
                case TypeString.UInt32:
                    result = new UnionBytesUInt32();
                    break;
                case TypeString.UInt64:
                    result = new UnionBytesUInt64();
                    break;
                case TypeString.Single:
                    result = new UnionBytesSingle();
                    break;
                case TypeString.Double:
                    result = new UnionBytesDouble();
                    break;

                default:
#if DEBUG
                    Debug.WriteLine($"don't support {typeof(T).Name} type.");
#endif
                    break;
            }

            return result;

        }









        static byte[] Struct2Bytes(object obj)
        {
            int rawsize = Marshal.SizeOf(obj);
            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.StructureToPtr(obj, buffer, false);
            byte[] rawdatas = new byte[rawsize];
            Marshal.Copy(buffer, rawdatas, 0, rawsize);
            Marshal.FreeHGlobal(buffer);
            return rawdatas;
        }

        static T Bytes2Struct<T>(byte[] rawdatas)
        {
            Type anytype = typeof(T);
            int rawsize = Marshal.SizeOf(anytype);
            if (rawsize > rawdatas.Length)
                return default(T);

            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.Copy(rawdatas, 0, buffer, rawsize);
            object retobj = Marshal.PtrToStructure(buffer, anytype);
            Marshal.FreeHGlobal(buffer);

            return (T)retobj;
        }



        // {{END}}
    }





}
